package com.clp.protocol.iec104.definition;

import com.clp.protocol.core.exception.EnumElemDoesNotExistException;
import com.clp.protocol.iec104.apdu.asdu.info_obj.info_elem.*;
import com.clp.protocol.iec104.apdu.asdu.info_obj.qua.*;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * I帧的类型标识定义。注意：类型标识和xxxInfoElem.class要一一对应
 */
@Getter
@AllArgsConstructor
public enum TypeTag {
    /* 在监视方向上的过程信息 */
    /**
     * 单点信息
     */
    M_SP_NA_1((byte) 1, "单点信息", 1, M_SP_NA_1_InfoElem.class, null),
    /**
     * 带时标的单点信息
     */
    M_SP_TA_1((byte) 2, "带时标的单点信息", 1, M_SP_TA_1_InfoElem.class, null),
    /**
     * 双点信息
     */
    M_DP_NA_1((byte) 3, "双点信息", 1, M_DP_NA_1_InfoElem.class, null),
    /**
     * 带时标的双点信息
     */
    M_DP_TA_1((byte) 4, "带时标的双点信息", 1, M_DP_TA_1_InfoElem.class, null),
    /**
     * 步位置信息
     */
    M_ST_NA_1((byte) 5, "步位置信息", 0, null, null),
    /**
     * 带时标的步位置信息
     */
    M_ST_TA_1((byte) 6, "带时标的步位置信息", 0, null, null),
    /**
     * 32位比特串
     */
    M_BO_NA_1((byte) 7, "32位比特串", 0, null, null),
    /**
     * 带时标的32比特串
     */
    M_BO_TA_1((byte) 8, "带时标的32比特串", 0, null, null),
    /**
     * 测量值，归一化值
     */
    M_ME_NA_1((byte) 9, "测量值，归一化值", 2, M_ME_NA_1_InfoElem.class, M_ME_NA_1_Qua.class),
    /**
     * 带时标的测量值，归一化值
     */
    M_ME_TA_1((byte) 10, "带时标的测量值，归一化值", 2, M_ME_TA_1_InfoElem.class, M_ME_TA_1_Qua.class),
    /**
     * 测量值，标度化值
     */
    M_ME_NB_1((byte) 11, "测量值，标度化值", 0, null, M_ME_NB_1_Qua.class),
    /**
     * 带时标的测量值，标度化值
     */
    M_ME_TB_1((byte) 12, "带时标的测量值，标度化值", 0, null, M_ME_TB_1_Qua.class),
    /**
     * 测量值，短浮点数
     */
    M_ME_NC_1((byte) 13, "测量值，短浮点数", 4, M_ME_NC_1_InfoElem.class, M_ME_NC_1_Qua.class),
    /**
     * 带时标的测量值，短浮点数
     */
    M_ME_TC_1((byte) 14, "带时标的测量值，短浮点数", 4, M_ME_TC_1_InfoElem.class, M_ME_TC_1_Qua.class),
    /**
     * 累积量（遥脉）
     */
    M_IT_NA_1((byte) 15, "累积量", 4, M_IT_NA_1_InfoElem.class, M_IT_NA_1_Qua.class),
    /**
     * 带时标的累积量
     */
    M_IT_TA_1((byte) 16, "带时标的累积量", 0, null, null),
    /**
     * 带时标的继电保护设备事件
     */
    M_EP_TA_1((byte) 17, "带时标的继电保护设备事件", 0, null, null),
    /**
     * 带时标的继电保护成组启动事件
     */
    M_EP_TB_1((byte) 18, "带时标的继电保护成组启动事件", 0, null, null),
    /**
     * 带时标的继电保护设备成组输出电路信息
     */
    M_EP_TC_1((byte) 19, "带时标的继电保护设备成组输出电路信息", 0, null, null),
    /**
     * 带变位检出成组单点信息
     */
    M_PS_NA_1((byte) 20, "带变位检出成组单点信息", 0, null, null),
    /**
     * 测量值，不带品质描述词的归一化值
     */
    M_ME_ND_1((byte) 21, "测量值，不带品质描述词的归一化值", 2, M_ME_ND_1_InfoElem.class, null),
    /**
     * 带CP56Time2a时标的单点信息
     */
    M_SP_TB_1((byte) 30, "带CP56Time2a时标的单点信息", 1, M_SP_TB_1_InfoElem.class, null),
    /**
     * 带CP56Time2a时标的双点信息
     */
    M_DP_TB_1((byte) 31, "带CP56Time2a时标的双点信息", 1, M_DP_TB_1_InfoElem.class, null),
    /**
     * 带CP56Time2a时标的步位信息
     */
    M_ST_TB_1((byte) 32, "带CP56Time2a时标的步位信息", 0, null, null),
    /**
     * 带CP56Time2a时标的32比特串
     */
    M_BO_TB_1((byte) 33, "带CP56Time2a时标的32比特串", 0, null, null),
    /**
     * 带CP56Time2a时标的测量值，归一化值
     */
    M_ME_TD_1((byte) 34, "带CP56Time2a时标的测量值，归一化值", 2, M_ME_TD_1_InfoElem.class, null),
    /**
     * 带CP56Time2a时标的测量值，标度化值
     */
    M_ME_TE_1((byte) 35, "带CP56Time2a时标的测量值，标度化值", 2, null, null),
    /**
     *
     */
    M_ME_TF_1((byte) 36, "带CP56Time2a时标的测量值，短浮点数", 4, M_ME_TF_1_InfoElem.class, M_ME_TF_1_Qua.class),
    /**
     * 带CP56Time2a时标的累积量
     */
    M_IT_TB_1((byte) 37, "带CP56Time2a时标的累积量", 0, null, null),
    /**
     * 带CP56Time2a时标的继电保护装置事件
     */
    M_EP_TD_1((byte) 38, "带CP56Time2a时标的继电保护装置事件", 0, null, null),
    /**
     * 带CP56Time2a时标的继电保护装置成组启动事件
     */
    M_EP_TE_1((byte) 39, "带CP56Time2a时标的继电保护装置成组启动事件", 0, null, null),
    /**
     * 带CP56Time2a时标的继电保护装置成组输出电路信息
     */
    M_EP_TF_1((byte) 40, "带CP56Time2a时标的继电保护装置成组输出电路信息", 0, null, null),

    /* 配网特殊 */
    /**
     * 故障事件信息
     */
    M_FT_NA_1((byte) 42, "故障事件信息", 0, null, null),
    /**
     * 累积量，短浮点数
     */
    M_IT_NB_1((byte) 206, "累积量，短浮点数", 0, null, null),
    /**
     * 带CP56Time2a时标的累积量，短浮点数
     */
    M_IT_TC_1((byte) 207, "带CP56Time2a时标的累积量，短浮点数", 0, null, null),

    /* 在控制方向的过程信息 */
    /**
     * 单点命令 遥控
     */
    C_SC_NA_1((byte) 45, "单点命令 遥控", 1, C_SC_NA_1_InfoElem.class, null),
    /**
     * 双点命令 遥控
     */
    C_DC_NA_1((byte) 46, "双点命令 遥控", 1, C_DC_NA_1_InfoElem.class, null),
    /**
     * 调节步命令
     */
    C_RC_NA_1((byte) 47, "调节步命令", 0, null, null),
    /**
     * 设定值命令，归一化值
     */
    C_SE_NA_1((byte) 48, "设定值命令（遥调），归一化值", 2, C_SE_NA_1_InfoElem.class, C_SE_NA_1_Qua.class),
    /**
     * 设定值命令，标度化值
     */
    C_SE_NB_1((byte) 49, "设定值命令（遥调），标度化值", 2, C_SE_NB_1_InfoElem.class, C_SE_NB_1_Qua.class),
    /**
     * 设定值命令，短浮点数
     */
    C_SE_NC_1((byte) 50, "设定值命令（遥调），短浮点数", 4, C_SE_NC_1_InfoElem.class, C_SE_NC_1_Qua.class),
    /**
     * 32位比特串
     */
    C_BO_NA_1((byte) 51, "32位比特串", 0, null, null),
    /**
     * 带CP56Time2a时标的单点命令
     */
    C_SC_TA_1((byte) 58, "带CP56Time2a时标的单点命令", 1, null, null),
    /**
     * 带CP56Time2a时标的双点命令
     */
    C_DC_TA_1((byte) 59, "带CP56Time2a时标的双点命令", 1, null, null),
    /**
     * 带CP56Time2a时标的步调节命令
     */
    C_RC_TA_1((byte) 60, "带CP56Time2a时标的步调节命令", 0, null, null),
    /**
     * 带CP56Time2a时标的设定值，归一化值
     */
    C_SE_TA_1((byte) 61, "带CP56Time2a时标的设定值，归一化值", 0, null, null),
    /**
     * 带CP56Time2a时标的设定值，标度化值
     */
    C_SE_TB_1((byte) 62, "带CP56Time2a时标的设定值，标度化值", 0, null, null),
    /**
     * 带CP56Time2a时标的设定值，短浮点数
     */
    C_SE_TC_1((byte) 63, "带CP56Time2a时标的设定值，短浮点数", 0, null, null),
    /**
     * 带CP56Time2a时标的32位比特串
     */
    C_BO_TC_1((byte) 64, "带CP56Time2a时标的32位比特串", 0, null, null),

    /* 在监视方向的系统命令 */
    /**
     * 初始化结束
     */
    M_EI_NA_1((byte) 70, "初始化结束", 0, null, M_EI_NA_1_Qua.class),

    /* 在控制方向的系统命令 */
    /**
     * 总召唤命令
     */
    C_IC_NA_1((byte) 100, "总召唤命令", 0, null, C_IC_NA_1_Qua.class),
    /**
     * 计数量/电度量召唤命令
     */
    C_CI_NA_1((byte) 101, "计数量召唤命令", 0, null, C_CI_NA_1_Qua.class),
    /**
     * 读命令，读单个参数
     */
    C_RD_NA_1((byte) 102, "读命令，读单个参数", 0, null, null),
    /**
     * 时钟同步命令
     */
    C_CS_NA_1((byte) 103, "时钟同步命令", 0, null, null),
    /**
     * 测试命令，平衡方式下才有的功能
     */
    C_TS_NA_1((byte) 104, "测试命令，平衡方式下才有的功能", 0, null, null),
    /**
     * 复位进程命令
     */
    C_RP_NA_1((byte) 105, "复位进程命令", 0, null, C_RP_NA_1_Qua.class),
    /**
     * 延时获得命令
     */
    C_CD_NA_1((byte) 106, "延时获得命令", 0, null, null),
    /**
     * 带CP56Time2a时标的测试命令
     */
    C_TS_TA_1((byte) 107, "带CP56Time2a时标的测试命令", 0, null, null),

    /* 在控制方向的参数命令 */
    /**
     * 测量值参数，归一化值
     */
    P_ME_NA_1((byte) 110, "测量值参数，归一化值", 0, null, null),
    /**
     * 测量值参数，标度化值
     */
    P_ME_NB_1((byte) 111, "测量值参数，标度化值", 0, null, null),
    /**
     * 测量值参数，短浮点数
     */
    P_ME_NC_1((byte) 112, "测量值参数，短浮点数", 0, null, null),
    /**
     * 参数激活
     */
    P_AC_NA_1((byte) 113, "参数激活", 0, null, null),

    /* 文件传输 */
    /**
     * 文件准备就绪
     */
    F_FR_NA_1((byte) 120, "文件准备就绪", 0, null, null),
    /**
     * 节准备就绪
     */
    F_SR_NA_1((byte) 121, "节准备就绪", 0, null, null),
    /**
     * 召唤目录，选择文件，召唤文件，召唤节
     */
    F_SC_NA_1((byte) 122, "召唤目录，选择文件，召唤文件，召唤节", 0, null, null),
    /**
     * 最后的节，最后的段
     */
    F_LS_NA_1((byte) 123, "最后的节，最后的段", 0, null, null),
    /**
     * 认可文件，认可节
     */
    F_AF_NA_1((byte) 124, "认可文件，认可节", 0, null, null),
    /**
     * 段
     */
    F_SG_NA_1((byte) 125, "段", 0, null, null),
    /**
     * 目录
     */
    F_DR_NA_1((byte) 126, "目录", 0, null, null),

    /* 配网特殊 */
    /**
     * 读多个参数命令
     */
    C_RD_NA_2((byte) 132, "读多个参数命令", 4, null, null),
    /**
     * 预置/激活多个参数命令，归一化值
     */
    C_SE_NA_2((byte) 136, "预置/激活多个参数命令，归一化值", 4, null, null),
    /**
     * 多点设定命令，浮点数
     */
    C_SE_NC_2((byte) 137, "多点设定命令，浮点数", 0, null, null),

    /* 调试工具特殊用 */
    /**
     * 通信参数读取，4字节整数
     */
    M_ME_NA_2((byte) 133, "通信参数读取，4字节整数", 4, null, null),
    /**
     * 通信参数设置，4字节整数
     */
    C_SE_NA_3((byte) 138, "通信参数设置，4字节整数", 4, null, null),

    /**
     * 切换定值区
     */
    C_SR_NA_1((byte) 200, "切换定值区", 0, null, null),
    /**
     * 读定值区号
     */
    C_RR_NA_1((byte) 201, "读定值区号", 0, null, null),
    /**
     * 读参数和定值
     */
    C_RS_NA_1((byte) 202, "读参数和定值", 0, null, null),
    /**
     * 写参数和定值
     */
    C_WS_NA_1((byte) 203, "写参数和定值", 0, null, null),
    /**
     * 文件传输
     */
    F_FR_NA_2((byte) 210, "文件传输", 0, F_FR_NA_2_InfoElem.class, null),
    /**
     * 软件升级
     */
    F_SR_NA_2((byte) 211, "软件升级", 0, null, null),

    /* 调试工具调试操作 */
    /**
     * 调试操作命令
     */
    C_SE_NA_4((byte) 225, "调试操作命令", 0, null, null),
    /**
     * 人工置数
     */
    C_SE_NA_5((byte) 226, "人工置数", 0, null, null),
    /**
     * 读分组参数和定值
     */
    C_READ_PARAM((byte) 227, "读分组参数和定值", 0, null, null),
    /**
     * 写分组参数和定值
     */
    C_WRITE_PARAM((byte) 228, "写分组参数和定值", 0, null, null);

    /**
     * 类型标识的设定值
     */
    private final byte val;
    private final String desc;
    /**
     * 该类型标识对应的单个信息元素的字节数
     */
    private final int infoElemBytesLen;
    /**
     * 对应的信息元素类型
     */
    private final Class<? extends InfoElem> infoElemClass;
    /**
     * 对应的限定词类型
     */
    private final Class<? extends Qua> quaClass;

    public InfoElem newInvalidInfoElem() {
        if (infoElemClass == null) {
            throw new RuntimeException("没有定义类型标识 " + this + "(" + this.getDesc() + ") 的信息元素类！");
        }
        InfoElem retInfoElem = null;
        try {
            Constructor<? extends InfoElem> constructor = infoElemClass.getConstructor();
            constructor.setAccessible(true);
            retInfoElem = constructor.newInstance();
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("没有无参构造！");
        }
        return retInfoElem;
    }

    public Qua newInvalidQua() {
        if (quaClass == null) {
            throw new RuntimeException("没有定义类型标识 " + this + "(" + this.getDesc() + ") 的限定词类！");
        }
        Qua qua = null;
        try {
            Constructor<? extends Qua> constructor = quaClass.getConstructor();
            constructor.setAccessible(true);
            qua = constructor.newInstance();
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("没有无参构造！");
        }
        return qua;
    }

    /**
     * 判断该类型是否为遥测类型（带时标、不带时标）
     *
     * @return
     */
    public boolean isTm() {
        return (this == M_ME_NA_1 ||          // 测量值，归一化值
                this == M_ME_TA_1 ||          // 带时标的测量值，归一化值
                this == M_ME_NB_1 ||          // 测量值，标度化值
                this == M_ME_TB_1 ||          // 带时标的测量值，标度化值
                this == M_ME_NC_1 ||          // 测量值，短浮点数
                this == M_ME_TC_1 ||          // 带时标的测量值，短浮点数
                this == M_ME_ND_1             // 测量值，不带品质描述词的归一化值
        );
    }

    /**
     * 判断该类型是否为遥测类型（带时标）
     */
    public boolean isTmWithTime2a() {
        return (this == M_ME_TA_1 ||          // 带时标的测量值，归一化值
                this == M_ME_TB_1 ||          // 带时标的测量值，标度化值
                this == M_ME_TC_1 ||          // 带时标的测量值，短浮点数
                this == M_ME_TF_1 ||
                this == M_ME_TD_1 ||
                this == M_ME_TE_1
        );
    }

    /**
     * 判断该类型是否是遥信类型（带时标、不带时标）
     */
    public boolean isTs() {
        return this.isOnePointTs() || this.isTwoPointTs();
    }

    public boolean isOnePointTs() {
        return (this == M_SP_NA_1 ||          // 单点信息
                this == M_SP_TA_1 ||          // 带时标的单点信息
                this == M_SP_TB_1             // 带CP56Time2a时标的单点信息
        );
    }

    public boolean isTwoPointTs() {
        return (this == M_DP_NA_1 ||          // 双点信息
                this == M_DP_TA_1 ||          // 带时标的双点信息
                this == M_DP_TB_1             // 带CP56Time2a时标的双点信息
        );
    }

    /**
     * 判断该类型是否是遥信类型（带时标）
     *
     * @return
     */
    public boolean isTsWithTime2a() {
        return (this == M_SP_TA_1 ||          // 带时标的单点信息
                this == M_SP_TB_1 ||          // 带CP56Time2a时标的单点信息
                this == M_DP_TA_1 ||          // 带时标的双点信息
                this == M_DP_TB_1             // 带CP56Time2a时标的双点信息
        );
    }

    /**
     * 判断该类型是否是遥控类型
     */
    public boolean isTc() {
        return (this == C_SC_NA_1 ||        // 单点命令，遥控
                this == C_DC_NA_1           // 双点命令，遥控
        );
    }

    public boolean isTp() {
        return (this == M_IT_NA_1   // 累积量（遥脉）
        );
    }

    /**
     * 判断是否是遥调
     *
     * @return
     */
    public boolean isTa() {
        return (this == C_SE_NA_1   // 设定值命令，归一化值
                || this == C_SE_NB_1    // 设定值命令，标度化值
                || this == C_SE_NC_1    // 设定值命令，短浮点数
        );
    }

    /**
     * 判断是否是参数设置
     */
    public boolean isPrefabParam() {
        return (false
        );
    }

    /**
     * 判断该类型是否是总召唤类型
     */
    public boolean isTotalCall() {
        return (this == C_IC_NA_1 || // 总召唤
                this == C_CI_NA_1 // 电度量召唤
        );
    }

    /**
     * 判断该类型是否是时钟同步
     */
    public boolean isClockSync() {
        return (this == C_CS_NA_1
        );
    }

    /**
     * 判断是否是复位进程
     */
    public boolean isResetProcess() {
        return (this == C_RP_NA_1
        );
    }

    /**
     * 判断是否是初始化结束
     */
    public boolean isInitEnd() {
        return (this == M_EI_NA_1
        );
    }

    /**
     * 判断是否是文件传输
     */
    public boolean isFileTransfer() {
        return (this == F_SC_NA_1       // 文件传输
                || this == F_FR_NA_2    // 召唤目录
                || this == F_DR_NA_1    // 目录
        );
    }

    /**
     * 判断该类型标识的可变结构限定词是否有效
     */
    public boolean hasVsq() {
        return !(this.isFileTransfer()      // 文件传输无此项
        );
    }

    /**
     * 判断该类型标识是否有信息元素
     */
    public boolean hasInfoElem() {
        return !(this.isTotalCall()                 // 总召唤无此项
                || this.isClockSync()               // 时钟同步无此项
                || this.isResetProcess()            // 复位进程无此项
                || this.isInitEnd()                 // 初始化结束无此项
        );
    }

    /**
     * 判断该类型标识是否有限定词
     */
    public boolean hasQua() {
        return !(this.isClockSync()         // 时钟同步无此项
                || this.isTs()      // 遥信无此项
                || this.isTc()     // 遥控无此项
                || this == F_FR_NA_2    // 文件传输无此项
                || this == M_ME_ND_1 // 测量值，不带品质描述词的归一化值
        );
    }

    /**
     * 判断该类型标识是否有时标
     */
    public boolean hasTime2a() {
        return (this.isClockSync()                  // 时钟同步 有此项
                || this.isTsWithTime2a() // 带时标的遥信 有此项
                || this.isTmWithTime2a()  // 带时标的遥测 有此项
        );
    }

    /**
     * 根据value值来返回对应的Type
     *
     * @param val
     * @return
     */
    public static TypeTag gain(byte val) {
        for (TypeTag typeTag : TypeTag.values()) {
            if (typeTag.getVal() == val) {
                return typeTag;
            }
        }
        throw new EnumElemDoesNotExistException(TypeTag.class);
    }

    public static TypeTag gainByInfoElemClass(Class<? extends InfoElem> clazz) {
        for (TypeTag typeTag : TypeTag.values()) {
            if (typeTag.getInfoElemClass() == clazz) {
                return typeTag;
            }
        }
        throw new EnumElemDoesNotExistException(TypeTag.class);
    }

    public static TypeTag gainByQuaClass(Class<? extends Qua> clazz) {
        for (TypeTag typeTag : TypeTag.values()) {
            if (typeTag.getQuaClass() == clazz) {
                return typeTag;
            }
        }
        throw new EnumElemDoesNotExistException(TypeTag.class);
    }
}
